package com.zmn.plat.business.impl.area;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.zmn.common.dto2.ResponseDTO;
import com.zmn.tapi.common.baidu.GeocodingResult;
import com.zmn.tapi.common.baidu.Location;
import com.zmn.tapi.common.baidu.ReverseGeocodingResult;
import com.zmn.tapi.dubbo.DubboConsts;
import com.zmn.tapi.dubbo.interfaces.baidu.BaiduMapRemoteService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.jetbrains.annotations.NotNull;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.data.annotation.Reference;
import org.springframework.stereotype.Service;

import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dictionary.DictModel;
import com.zmn.common.dto.VtDTO;
import com.zmn.common.utils.map.MapUtils;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.manager.common.database.annotation.ReadOnlyConnection;
import com.zmn.manager.common.interfaces.map.BaiduMapManager;
import com.zmn.plat.business.interfaces.area.BaseAreaBService;
import com.zmn.plat.common.dictionary.PlatErrorCodeDict;
import com.zmn.plat.common.dto.area.AreaDRO;
import com.zmn.plat.common.exception.PlatException;
import com.zmn.plat.manager.interfaces.cache.AreaCache;
import com.zmn.plat.model.entity.area.AreaQuery;
import com.zmn.plat.model.entity.area.BaseArea;
import com.zmn.plat.services.interfaces.area.BaseAreaService;

import lombok.extern.slf4j.Slf4j;

/**
 * Creator: nowind
 *
 * @since 2018/7/12 16:15
 */
@Service
@Slf4j
public class BaseAreaBServiceImpl implements BaseAreaBService {

    @Resource
    private BaseAreaService areaService;
    @Resource
    private AreaCache areaCache;
    @Resource
    BaiduMapManager baiduMapManager;

    @DubboReference(version = DubboConsts.INTERFACE_VERSION, check = false)
    private BaiduMapRemoteService baiduMapRemoteService;

    private BeanCopier copier = BeanCopier.create(BaseArea.class, AreaDRO.class, false);

    @Override
    public Integer updateByKey(BaseArea area) {
        return areaService.updateByKey(area);
    }

    @Override
    public Integer insert(BaseArea area) {
        int pos = areaService.insert(area);
        return pos > 0 ? area.getAreaId() : 0;
    }

    @Override
    @ReadOnlyConnection
    @Cacheable(key = "'dubbo:areas:pid:'+#p0", cacheNames = "redis1h")
    public List<VtDTO> getAreaList(Integer parentId) throws PlatException {
        if (parentId == null) {
            parentId = GlobalConsts.TOP_ID;
        }

        List<VtDTO> dtos;
        try {
            List<DictModel> items = areaCache.getAreaList(parentId);

            dtos = getVtDTOS(items);
        } catch (Exception e) {

            log.error("查询失败, exception:{}, msg:{}", e, e.getMessage());
            throw new PlatException(e.getMessage());
        }

        return dtos;
    }

    @Override
    // @Cacheable(key = "'dubbo:areaAndParent:'+#p0", cacheNames = "redis1h")
    public AreaDRO getAreaAndParent(Integer areaId) {
        BaseArea area = areaService.findByKey(areaId);

        if (area.getLevel() == GlobalConsts.AREA_STREET2) {
            area.setStreetId(area.getAreaId());
            area.setStreetName(area.getName());

            BaseArea county = areaService.findByKey(area.getParentId());
            area.setCountyId(county.getAreaId());
            area.setCountyName(county.getName());

            BaseArea city = areaService.findByKey(county.getParentId());
            area.setCityId(city.getAreaId());
            area.setCityName(city.getName());

            BaseArea province = areaService.findByKey(city.getParentId());
            area.setProvinceId(province.getAreaId());
            area.setProvinceName(province.getName());
        }

        if (area.getLevel() == GlobalConsts.AREA_COUNTY) {
            area.setCountyId(area.getAreaId());
            area.setCountyName(area.getName());

            BaseArea city = areaService.findByKey(area.getParentId());
            area.setCityId(city.getAreaId());
            area.setCityName(city.getName());

            BaseArea province = areaService.findByKey(city.getParentId());
            area.setProvinceId(province.getAreaId());
            area.setProvinceName(province.getName());
        }

        if (area.getLevel() == GlobalConsts.AREA_CITY) {
            area.setCityId(area.getAreaId());
            area.setCityName(area.getName());

            BaseArea province = areaService.findByKey(area.getParentId());
            area.setProvinceId(province.getAreaId());
            area.setProvinceName(province.getName());
        }

        AreaDRO dro = new AreaDRO();
        copier.copy(area, dro, null);

        return dro;
    }

    @NotNull
    @ReadOnlyConnection
    private List<VtDTO> getVtDTOS(List<DictModel> items) {
        List<VtDTO> dtos = new ArrayList<>();

        items.forEach(dictModel -> {
            dtos.add(new VtDTO(dictModel.getKey(), dictModel.getValue()));
        });
        return dtos;
    }

    @Override
    public AreaDRO getAreaByAddress(String address) {
        if (StringUtil.isBlank(address)) {
            log.error("地址不能为空，address:{}，errorCode={}", address, PlatErrorCodeDict.ERROR_CODE_ADDRESS_NULL);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_ADDRESS_NULL);
        }

        // 获取地址坐标
        ResponseDTO<GeocodingResult> geocodingResultResponseDTO = baiduMapRemoteService.geocoding(address, null);
        if (Objects.isNull(geocodingResultResponseDTO) || !geocodingResultResponseDTO.isSuccess()) {
            log.error("BaiduMapRemoteService#geocoding接口异常[{}]，返回值", geocodingResultResponseDTO);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_THIRD_SERVICE_ERROR);
        }
        GeocodingResult geocodingResult = geocodingResultResponseDTO.getData();
        Location location = geocodingResult.getLocation();

        // 根据精度维度定位区域
        ResponseDTO<ReverseGeocodingResult> reverseResponseDTO = baiduMapRemoteService.reverseGeocoding(location.getLat().toString(), location.getLng().toString());
        if (Objects.isNull(reverseResponseDTO) || !reverseResponseDTO.isSuccess()) {
            log.error("BaiduMapRemoteService#reverseGeocoding接口异常，返回值[{}]", reverseResponseDTO);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_THIRD_SERVICE_ERROR);
        }
        ReverseGeocodingResult responseDTOData = reverseResponseDTO.getData();
        int code = responseDTOData.getAddressComponent().getAdcode();

        //String code = baiduMapManager.getAreaCodeByAddress(address);

        if (code <= 0) {
            log.error("城市不存在，code:{},errorCode={}", code, PlatErrorCodeDict.ERROR_CODE_CITY_NOT_EXIST);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_CITY_NOT_EXIST);
        }

        BaseArea area = areaCache.getArea(code);

        if (Objects.isNull(area)) {
            log.error("城市不存在，area:{}，errorCode={}", area, PlatErrorCodeDict.ERROR_CODE_CITY_NOT_EXIST);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_CITY_NOT_EXIST);
        }

        AreaDRO dro = getAreaAndParent(area.getAreaId());

        return dro;
    }

    @Override
    public List<VtDTO> listAreaVtByParentIdAndStaffIdNoCache(Integer parentId, Integer staffId) {
        // -1表示要求数据权限但未通过身份校验
        if (NumberUtil.isNotNullOrZero(staffId) && staffId == -1) {
            log.error("身份校验失败,errorCode={}", PlatErrorCodeDict.ERROR_CODE_IDENTITY_CHECK_ERROR);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_IDENTITY_CHECK_ERROR);
        }

        // 参数校验
        if (NumberUtil.isNullOrZero(parentId)) {
            log.error("parentId不能为空,errorCode={}", PlatErrorCodeDict.ERROR_CODE_PANAM_NULL);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_PANAM_NULL);
        }

        AreaQuery areaQuery = new AreaQuery();
        areaQuery.setParentId(parentId);
        List<BaseArea> baseAreaList;
        if (NumberUtil.isPositiveInteger(staffId)) {
            baseAreaList = areaService.listByQueryOfPermit(areaQuery, staffId);
        } else {
            // 取得下级区域列表
            baseAreaList = areaService.listByQuery(areaQuery);
        }

        List<VtDTO> items = baseAreaList.stream().map(baseArea -> new VtDTO(baseArea.getAreaId(), baseArea.getName())).collect(Collectors.toList());

        return items;
    }

    @Override
    public Double getDistanceByDistrictIdAndLongitudeAndLatitude(Integer districtId, Double longitude, Double latitude) {
        if (districtId == null || longitude == null || latitude == null) {
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_PANAM_NULL);
        }

        double num;
        BaseArea area = areaCache.getArea(districtId);
        double lon = Double.parseDouble(area.getLongitude());
        double lat = Double.parseDouble(area.getLatitude());

        num = MapUtils.getDistance(lon, lat, longitude, latitude);

        return num;
    }

    @Override
    public List<VtDTO> listAreaVtByParentIdListAndStaffId(List<Integer> parentIdList, Integer staffId, Integer level) {
        // -1表示要求数据权限但未通过身份校验
        if (NumberUtil.isNotNullOrZero(staffId) && staffId == -1) {
            log.error("身份校验失败,errorCode={}", PlatErrorCodeDict.ERROR_CODE_IDENTITY_CHECK_ERROR);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_IDENTITY_CHECK_ERROR);
        }

        List<BaseArea> baseAreaList = areaService.listAreaByParentIdList(parentIdList, staffId, level);
        if (baseAreaList.isEmpty()) {
            return new ArrayList<>();
        }
        return baseAreaList.stream().map(baseArea -> new VtDTO(baseArea.getAreaId(), baseArea.getName())).collect(Collectors.toList());
    }
}
